<?php

namespace org\copperforest\oak\sql;

class SQLEngineHelper
{

    private $engine;
    private $version;
    private $reserveds;

    function __construct( $engine, $version = null )
    {
        $this->engine = strtolower( $engine );
        $this->version = strtolower( $version );

        $this->reserveds = array( );

        switch ( $this->engine ) {


            case 'mssql':

                $odbc = array( "ABSOLUTE", "ACTION", "ADA", "ADD", "ALL", "ALLOCATE",
                    "ALTER", "AND", "ANY", "ARE", "AS", "ASC", "ASSERTION", "AT",
                    "AUTHORIZATION", "AVG", "BEGIN", "BETWEEN", "BIT", "BIT_LENGTH",
                    "BOTH", "BY", "CASCADE", "CASCADED", "CASE", "CAST", "CATALOG",
                    "CHAR", "CHAR_LENGTH", "CHARACTER", "CHARACTER_LENGTH", "CHECK",
                    "CLOSE", "COALESCE", "COLLATE", "COLLATION", "COLUMN", "COMMIT",
                    "CONNECT", "CONNECTION", "CONSTRAINT", "CONSTRAINTS", "CONTINUE",
                    "CONVERT", "CORRESPONDING", "COUNT", "CREATE", "CROSS", "CURRENT",
                    "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER",
                    "CURSOR",
                    "DATE", "DAY", "DEALLOCATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT",
                    "DEFERRABLE", "DEFERRED", "DELETE", "DESC", "DESCRIBE", "DESCRIPTOR",
                    "DIAGNOSTICS", "DISCONNECT", "DISTINCT", "DOMAIN", "DOUBLE",
                    "DROP", "ELSE", "END", "END-EXEC", "ESCAPE", "EXCEPT", "EXCEPTION",
                    "EXEC", "EXECUTE", "EXISTS", "EXTERNAL", "EXTRACT", "FALSE",
                    "FETCH", "FIRST", "FLOAT", "FOR", "FOREIGN", "FORTRAN", "FOUND",
                    "FROM", "FULL",
                    "GET", "GLOBAL", "GO", "GOTO", "GRANT", "GROUP", "HAVING", "HOUR",
                    "IDENTITY", "IMMEDIATE", "IN", "INCLUDE", "INDEX", "INDICATOR",
                    "INITIALLY", "INNER", "INPUT", "INSENSITIVE", "INSERT", "INT",
                    "INTEGER", "INTERSECT", "INTERVAL", "INTO", "IS", "ISOLATION",
                    "JOIN", "KEY", "LANGUAGE", "LAST", "LEADING", "LEFT", "LEVEL",
                    "LIKE", "LOCAL", "LOWER", "MATCH", "MAX", "MIN", "MINUTE", "MODULE",
                    "MONTH", "NAMES", "NATIONAL", "NATURAL", "NCHAR", "NEXT", "NO",
                    "NONE", "NOT", "NULL", "NULLIF", "NUMERIC",
                    "OCTET_LENGTH", "OF", "ON", "ONLY", "OPEN", "OPTION", "OR", "ORDER",
                    "OUTER", "OUTPUT", "OVERLAPS", "PAD", "PARTIAL", "PASCAL", "POSITION",
                    "PRECISION", "PREPARE", "PRESERVE", "PRIMARY", "PRIOR", "PRIVILEGES",
                    "PROCEDURE", "PUBLIC", "READ", "REAL", "REFERENCES", "RELATIVE",
                    "RESTRICT", "REVOKE", "RIGHT", "ROLLBACK", "ROWS", "SCHEMA",
                    "SCROLL", "SECOND", "SECTION", "SELECT", "SESSION", "SESSION_USER",
                    "SET", "SIZE", "SMALLINT", "SOME", "SPACE", "SQL", "SQLCA", "SQLCODE",
                    "SQLERROR", "SQLSTATE", "SQLWARNING", "SUBSTRING", "SUM", "SYSTEM_USER",
                    "TABLE", "TEMPORARY", "THEN", "TIME", "TIMESTAMP", "TIMEZONE_HOUR",
                    "TIMEZONE_MINUTE", "TO", "TRAILING", "TRANSACTION", "TRANSLATE",
                    "TRANSLATION", "TRIM", "TRUE", "UNION", "UNIQUE", "UNKNOWN",
                    "UPDATE", "UPPER", "USAGE", "USER", "USING", "VALUE", "VALUES",
                    "VARCHAR", "VARYING", "VIEW", "WHEN", "WHENEVER", "WHERE", "WITH",
                    "WORK", "WRITE", "YEAR", "ZONE" );


                $mssql2005 = array( "BACKUP", "BREAK", "BROWSE", "BULK", "CHECKPOINT",
                    "CLUSTERED", "COMPUTE", "CONTAINSTABLE",
                    "DATABASE", "DBCC", "DENY", "DISK", "DISTRIBUTED", "DUMP", "ERRLVL",
                    "EXIT", "FILE", "FILLFACTOR", "FREETEXT", "FREETEXTTABLE", "FUNCTION",
                    "HOLDLOCK",
                    "IDENTITY_INSERT", "IDENTITYCOL", "IF", "LINENO", "LOAD", "NOCHECK",
                    "NONCLUSTERED",
                    "OFF", "OFFSETS", "OPENDATASOURCE", "OPENQUERY", "OPENROWSET",
                    "OPENXML", "OVER", "PERCENT", "PIVOT", "PLAN", "PRINT", "PROC",
                    "RAISERROR", "READTEXT",
                    "RECONFIGURE", "REPLICATION", "RESTORE", "RETURN", "REVERT",
                    "ROWCOUNT", "ROWGUIDCOL", "RULE", "SAVE", "SECURITYAUDIT", "SETUSER",
                    "SHUTDOWN", "STATISTICS", "SYSTEM_USER",
                    "TABLESAMPLE", "TEXTSIZE", "TOP", "TRAN", "TRIGGER", "TRUNCATE",
                    "TSEQUAL", "UNPIVOT", "UPDATETEXT", "USE", "VALUES", "WAITFOR",
                    "WHILE", "WRITETEXT" );

                $mssql2008 = array( "MERGE" );

                $mssql2012 = array( "SEMANTICKEYPHRASETABLE", "SEMANTICSIMILARITYDETAILSTABLE",
                    "SEMANTICSIMILARITYTABLE", "TRY_CONVERT", "TRY_CONVERT" );

                /*
                  $future=array("ADMIN","AFTER","AGGREGATE","ALIAS","ALLOCATE","ARE","ARRAY","ASENSITIVE","ASSERTION","ASYMMETRIC","AT","ATOMIC","BEFORE","BINARY","BIT","BLOB","BOOLEAN","BOTH","BREADTH","CALL","CALLED","CARDINALITY","CASCADED","CAST","CATALOG","CHAR","CHARACTER","CLASS","CLOB","COLLATION","COLLECT","COMPLETION","CONDITION","CONNECT","CONNECTION","CONSTRAINTS","CONSTRUCTOR","CORR","CORRESPONDING","COVAR_POP","COVAR_SAMP","CUBE","CUME_DIST","CURRENT_CATALOG","CURRENT_DEFAULT_TRANSFORM_GROUP","CURRENT_PATH","CURRENT_ROLE","CURRENT_SCHEMA","CURRENT_TRANSFORM_GROUP_FOR_TYPE","CYCLE",

                  "DATA","DATE","DAY","DEC","DECIMAL","DEFERRABLE","DEFERRED","DEPTH","DEREF","DESCRIBE","DESCRIPTOR","DESTROY","DESTRUCTOR","DETERMINISTIC","DICTIONARY","DIAGNOSTICS","DISCONNECT","DOMAIN","DYNAMIC","EACH","ELEMENT","END-EXEC","EQUALS","EVERY","EXCEPTION","FALSE","FILTER","FIRST","FLOAT","FOUND","FREE","FULLTEXTTABLE","FUSION","GENERAL","GET","GLOBAL","GO","GROUPING","HOLD","HOST","HOUR","IGNORE","IMMEDIATE","INDICATOR","INITIALIZE","INITIALLY","INOUT","INPUT","INT","INTEGER","INTERSECTION","INTERVAL","ISOLATION","ITERATE",
                  "LANGUAGE","LARGE","LAST","LATERAL","LEADING","LESS","LEVEL","LIKE_REGEX","LIMIT","LN","LOCAL","LOCALTIME","LOCALTIMESTAMP","LOCATOR","MAP","MATCH","MEMBER","METHOD","MINUTE","MOD","MODIFIES","MODIFY","MODULE","MONTH","MULTISET","NAMES","NATURAL","NCHAR","NCLOB","NEW","NEXT","NO","NONE","NORMALIZE","NUMERIC","OBJECT","OCCURRENCES_REGEX","OLD","ONLY","OPERATION","ORDINALITY","OUT","OVERLAY","OUTPUT","PAD","PARAMETER","PARAMETERS","PARTIAL","PARTITION","PATH","POSTFIX","PREFIX","PREORDER","PREPARE","PERCENT_RANK","PERCENTILE_CONT","PERCENTILE_DISC","POSITION_REGEX","PRESERVE","PRIOR","PRIVILEGES",
                  "RANGE","READS","REAL","RECURSIVE","REF","REFERENCING","REGR_AVGX","REGR_AVGY","REGR_COUNT","REGR_INTERCEPT","REGR_R2","REGR_SLOPE","REGR_SXX","REGR_SXY","REGR_SYY","RELATIVE","RELEASE","RESULT","RETURNS","ROLE","ROLLUP","ROUTINE","ROW","ROWS","SAVEPOINT","SCROLL","SCOPE","SEARCH","SECOND","SECTION","SENSITIVE","SEQUENCE","SESSION","SETS","SIMILAR","SIZE","SMALLINT","SPACE","SPECIFIC","SPECIFICTYPE","SQL","SQLEXCEPTION","SQLSTATE","SQLWARNING","START","STATE","STATEMENT","STATIC","STDDEV_POP","STDDEV_SAMP","STRUCTURE","SUBMULTISET","SUBSTRING_REGEX","SYMMETRIC","SYSTEM",
                  "TEMPORARY","TERMINATE","THAN","TIME","TIMESTAMP","TIMEZONE_HOUR","TIMEZONE_MINUTE","TRAILING","TRANSLATE_REGEX","TRANSLATION","TREAT","TRUE","UESCAPE","UNDER","UNKNOWN","UNNEST","USAGE","USING","VALUE","VAR_POP","VAR_SAMP","VARCHAR","VARIABLE","WHENEVER","WIDTH_BUCKET","WITHOUT","WINDOW","WITHIN","WORK","WRITE","XMLAGG","XMLATTRIBUTES","XMLBINARY","XMLCAST","XMLCOMMENT","XMLCONCAT","XMLDOCUMENT","XMLELEMENT","XMLEXISTS","XMLFOREST","XMLITERATE","XMLNAMESPACES","XMLPARSE","XMLPI","XMLQUERY","XMLSERIALIZE","XMLTABLE","XMLTEXT","XMLVALIDATE","YEAR","ZONE");
                 */

                if ( $this->version == '2005' )
                        $this->reserveds = array_merge( $odbc, $mssql2005 );

                elseif ( $this->version == '2008' || $this->version == '2008r2' )
                        $this->reserveds = array_merge( $odbc, $mssql2005,
                            $mssql2008 );

                else if ( $this->version == '2012' || $this->version == null )
                        $this->reserveds = array_merge( $odbc, $mssql2005,
                            $mssql2008, $mssql2012 );


                break;

            default:

                $this->engine = 'mysql';

                if ( (( float ) $this->version) >= 4 || $this->version == null )
                        $this->reserveds = array_merge( $this->reserveds,
                            array(
                        "ADD", "ALL", "ALTER", "ANALYZE", "AND", "AS", "ASC", "BEFORE",
                        "BETWEEN", "BIGINT", "BINARY", "BLOB", "BOTH", "BY", "CASCADE",
                        "CASE", "CHANGE", "CHAR", "CHARACTER", "CHECK", "COLLATE",
                        "COLUMN", "CONSTRAINT", "CONVERT", "CREATE", "CROSS", "CURRENT_DATE",
                        "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER",
                        "DATABASE", "DATABASES", "DAY_HOUR", "DAY_MICROSECOND", "DAY_MINUTE",
                        "DAY_SECOND", "DEC", "DECIMAL", "DEFAULT", "DELAYED", "DELETE",
                        "DESC", "DESCRIBE", "DISTINCT", "DISTINCTROW", "DIV", "DOUBLE",
                        "DROP", "DUAL", "ELSE", "ENCLOSED", "ESCAPED", "EXISTS",
                        "EXPLAIN",
                        "FALSE", "FLOAT", "FLOAT4", "FLOAT8", "FOR", "FORCE", "FOREIGN",
                        "FROM", "FULLTEXT", "GRANT", "GROUP", "HAVING", "HIGH_PRIORITY",
                        "HOUR_MICROSECOND", "HOUR_MINUTE", "HOUR_SECOND", "IF", "IGNORE",
                        "IN", "INDEX", "INFILE", "INNER", "INSERT", "INT", "INT1",
                        "INT2", "INT3", "INT4", "INT8", "INTEGER", "INTERVAL", "INTO",
                        "IS",
                        "JOIN", "KEY", "KEYS", "KILL", "LEADING", "LEFT", "LIKE",
                        "LIMIT", "LINES", "LOAD", "LOCALTIME", "LOCALTIMESTAMP",
                        "LOCK", "LONG", "LONGBLOB", "LONGTEXT", "LOW_PRIORITY", "MATCH",
                        "MEDIUMBLOB", "MEDIUMINT", "MEDIUMTEXT", "MIDDLEINT", "MINUTE_MICROSECOND",
                        "MINUTE_SECOND", "MOD", "NATURAL", "NOT", "NO_WRITE_TO_BINLOG",
                        "NULL", "NUMERIC",
                        "ON", "OPTIMIZE", "OPTION", "OPTIONALLY", "OR", "ORDER",
                        "OUTER", "OUTFILE", "PRECISION", "PRIMARY", "PROCEDURE",
                        "PURGE", "READ", "REAL", "REFERENCES", "REGEXP", "RENAME",
                        "REPLACE", "REQUIRE", "RESTRICT", "REVOKE", "RIGHT", "RLIKE",
                        "SECOND_MICROSECOND", "SELECT", "SEPARATOR", "SET", "SHOW",
                        "SMALLINT", "SONAME", "SPATIAL", "SQL_BIG_RESULT", "SQL_CALC_FOUND_ROWS",
                        "SQL_SMALL_RESULT", "SSL", "STARTING", "STRAIGHT_JOIN", "TABLE",
                        "TERMINATED", "THEN", "TINYBLOB", "TINYINT", "TINYTEXT",
                        "TO", "TRAILING", "TRUE",
                        "UNION", "UNIQUE", "UNLOCK", "UNSIGNED", "UPDATE", "USAGE",
                        "USE", "USING", "UTC_DATE", "UTC_TIME", "UTC_TIMESTAMP",
                        "VALUES", "VARBINARY", "VARCHAR", "VARCHARACTER", "VARYING",
                        "WHEN", "WHERE", "WITH", "WRITE", "XOR", "YEAR_MONTH", "ZEROFILL" ) );

                if ( (( float ) $this->version) >= 5.0 || $this->version == null )
                        $this->reserveds = array_merge( $this->reserveds,
                            array( "ASENSITIVE", "CALL", "CONDITION", "CONNECTION",
                        "CONTINUE", "CURSOR", "DECLARE", "DETERMINISTIC", "EACH",
                        "ELSEIF", "EXIT", "FETCH", "GOTO", "INOUT", "INSENSITIVE",
                        "ITERATE", "LABEL", "LEAVE", "LOOP", "MODIFIES", "OUT", "READS",
                        "RELEASE", "REPEAT", "RETURN", "SCHEMA", "SCHEMAS", "SENSITIVE",
                        "SPECIFIC", "SQL", "SQLEXCEPTION", "SQLSTATE", "SQLWARNING",
                        "TRIGGER", "UNDO", "UPGRADE", "WHILE" ) );

                if ( (( float ) $this->version) >= 5.2 || $this->version == null )
                        $this->reserveds = array_merge( $this->reserveds,
                            array( "ACCESSIBLE", "LINEAR", "MASTER_SSL_VERIFY_SERVER_CERT",
                        "RANGE", "READ_WRITE", "RESIGNAL", "SIGNAL" ) );

                if ( (( float ) $this->version) >= 5.5 || $this->version == null )
                        $this->reserveds = array_merge( $this->reserveds,
                            array( "GENERAL", "IGNORE_SERVER_IDS", "MASTER_HEARTBEAT_PERIOD",
                        "MAXVALUE", "SLOW" ) );

                break;
        }
    }

    function isDeadLockException( $code )
    {
        switch ( $this->engine ) {
            case 'mysql':
                return ($code == 1205 || $code == 1213);
            case 'mssql':
                return ($code == 'HYT00' || $code == 'S1T00'); //SQLSTATE
        }
    }

    function quoteIdentifier( $name )
    {
        if ( is_array( $name ) ) {

            switch ( $this->engine ) {
                case 'mysql':
                    foreach ( $name as &$n )
                            if ( !preg_match( '/^[a-z_][a-z0-9_]*$/i', $n ) || in_array( strtoupper( $n ),
                                        $this->reserveds ) )
                                $n = '`' . strtr( $n, array( '`' => '``' ) ) . '`';

                case 'mssql':
                    foreach ( $name as &$n )
                            if ( !preg_match( '/^[a-z_][a-z0-9_]*$/i', $n ) || in_array( strtoupper( $n ),
                                        $this->reserveds ) )
                                $n = '[' . strtr( $name, array( ']' => ']]' ) ) . ']';

                    return $name;
            }
        }
        else {

            switch ( $this->engine ) {
                case 'mysql':
                    if ( !preg_match( '/^[a-z_][a-z0-9_]*$/i', $name ) || in_array( strtoupper( $name ),
                                    $this->reserveds ) )
                            return '`' . strtr( $name, array( '`' => '``' ) ) . '`';
                    else return $name;
                case 'mssql':
                    if ( !preg_match( '/^[a-z_][a-z0-9_]*$/i', $name ) || in_array( strtoupper( $name ),
                                    $this->reserveds ) )
                            return '[' . strtr( $name, array( ']' => ']]' ) ) . ']';
                    else return $name;
            }
        }
    }

    function isNumeric( $type )
    {
        switch ( $this->engine ) {
            case 'mysql':
                return in_array( strtoupper( $type ),
                        array( 'INTEGER', 'SMALLINT', 'DECIMAL', 'NUMERIC', 'FLOAT',
                    'REAL', 'DOUBLE PRECISION', 'INT', 'DEC', 'TINYINT', 'MEDIUMINT',
                    'BIGINT', 'BIT' ) );
            case 'mssql':
                return in_array( strtoupper( $type ),
                        array( 'BIGINT', 'NUMERIC', 'BIT', 'SMALLINT', 'DECIMAL',
                    'SMALLMONEY', 'MONEY', 'INT', 'TINYINT', 'FLOAT', 'REAL' ) );
        }
    }

    function getIntegerType( $min, $max )
    {
        switch ( $this->engine ) {
            case 'mysql':
                if ( $min == 0 && $max == 1 ) return 'tinyint(1)';

                else if ( $max <= 255 && $min >= 0 ) return 'tinyint UNSIGNED';

                else if ( $max <= 127 && $min >= -128 ) return 'tinyint';

                else if ( $max <= 65535 && $min >= 0 )
                        return 'smallint UNSIGNED';

                else if ( $max <= 32767 && $min >= -32768 ) return 'smallint';

                else if ( $max <= 16777215 && $min >= 0 )
                        return 'mediumint UNSIGNED';

                else if ( $max <= 8388607 && $min >= -8388608 )
                        return 'mediumint';

                else if ( $max <= 2147483647 && $min >= -2147483648 )
                        return 'int';

                else if ( $max <= 18446744073709551615 && $min >= 0 )
                        return 'bigint UNSIGNED';

                else if ( $max <= 9223372036854775807 && $min >= -9223372036854775808 )
                        return 'bigint';

                return false;
                break;

            case 'mssql':

                if ( $min == 0 && $max == 1 ) return 'bit';

                else if ( $max <= 255 && $min >= 0 ) return 'tinyint';

                else if ( $max <= 32767 && $min >= 32768 ) return 'smallint';

                else if ( $max <= 2147483647 && $min >= 2147483648 )
                        return 'int';

                else if ( $max <= 9223372036854775807 && $min >= 9223372036854775808 )
                        return 'bigint';

                return false;
                break;
        }
    }

    function geDecimalType( $precision, $decimals )
    {
        return 'decimal(' . $precision . ',' . $decimals . ')';
    }

    function getFloatType( $decimals )
    {
        if ( $decimals <= 7 ) return 'float(24)';
        else return 'float(53)';
    }

    function autoincrement()
    {
        switch ( $this->engine ) {
            case 'mysql':
                return 'AUTO_INCREMENT';
            case 'mssql':
                return 'IDENTITY(1,1)';
        }
    }

    function time()
    {
        switch ( $this->engine ) {
            case 'mysql':
                return 'NOW()';
            case 'mssql':
                return 'NOW()';
        }
    }

    function isLimitAvailable()
    {
        switch ( $this->engine ) {
            case 'mysql':
                return true;
            case 'mssql':
                return false;
            case 'oracle':
                return false;
        }
    }

    function isForUpdateAvailable()
    {
        switch ( $this->engine ) {
            case 'mysql':
                return true;
            case 'mssql':
                return false;
            case 'oracle':
                return true;
        }
    }

    function isTopAvailable()
    {
        switch ( $this->engine ) {
            case 'mysql':
                return false;
            case 'mssql':
                return true;
            case 'oracle':
                return false;
        }
    }

    //En mysql cada catalogo es un usuario? lo que si puedo hacer es seleccionar un schema por defecto, en mssql depende del usuario el schema por defecto
    function getStorageEnginesSQL()
    {
        switch ( $this->engine ) {
            case 'mysql':
                return 'SELECT ENGINE FROM INFORMATION_SCHEMA.ENGINES WHERE support IN (\'YES\',\'DEFAULT\') AND TRANSACTIONS=\'YES\'';
            case 'mssql':
                return 'SELECT \'\'';
        }
    }

    /*
    function getCatalogAndSchema( $dsn ){
        $matches = array( );
        if ( preg_match( '/[;:]dbname=([^;]+)($|;)/', $dsn, $matches ) ){
            switch ( $this->engine ) {
                case 'mysql':
                    return array( 'def', $matches[ 1 ] );
                case 'mssql':
                    return array( $matches[ 1 ], null );
            }
        }
    }
    */
    
    function getSchemasSQL()
    {
        return 'SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA';
    }

    function getDefaultSchemaSQL()
    {
        switch ( $this->engine ) {
            case 'mysql':
                return 'SELECT DATABASE()';
            case 'mssql':
                return 'SELECT SCHEMA_NAME()';
        }
    }

    function getDefaultCatalogSQL()
    {
        switch ( $this->engine ) {
            case 'mysql':
                return 'SELECT \'def\'';
            case 'mssql':
                return 'SELECT DB_NAME()';
        }
    }

    function getTablesSQL( $schema = null )
    {
        return 'SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.Tables' . (empty( $schema ) ? '' : ' WHERE TABLE_SCHEMA=' . $this->quote( $schema ));
    }

    function getColumnsSQL( $table, $schema = null )
    {
        //TABLE_CATALOG, TABLE_SCHEMA
        return 'SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.Columns WHERE TABLE_NAME=' . $this->quote( $table ) . (empty( $schema ) ? '' : ' AND TABLE_SCHEMA=' . $this->quote( $schema ));
    }

    function getPrimaryKeysSQL( $table, $schema = null )
    {
        $sql = 'SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.';

        if ( $this->engine == 'mysql' )
                $sql.='COLUMNS WHERE COLUMN_KEY=\'PRI\'';
        else
                $sql = 'KEY_COLUMN_USAGE WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), \'IsPrimaryKey\') = 1';

        $sql.=' AND TABLE_NAME = ' . $this->quote( $table ) . (empty( $schema ) ? '' : ' AND TABLE_SCHEMA=' . $this->quote( $schema ));

        return $sql;
    }

    function getHomonymColumns( $tabla, $conexion_to, $conexion_from )
    {

        //FALTA LA BASE DE DATOs DE LA CONEXION
        $campos = $conexion_to->get_rows( $this->getColumnsSQL( $tabla,
                                $conexion_to->db ), true )->toArray();

        $camposto = array( );
        $isnumeric = array( );


        foreach ( $campos as $c ) {

            $camposto[ ] = $c[ 0 ];

            if ( $helper->isNumeric( $c[ 1 ] ) ) $isnumeric[ $c[ 0 ] ] = true;
            else $isnumeric[ $c[ 0 ] ] = false;
        }

        $campos = $conexion_from->get_rows( $this->getColumnsSQL( $tabla,
                                $conexion_from->db ), true )->toArray();

        $camposfrom = array( );
        foreach ( $campos as $c ) $camposfrom[ ] = $c[ 0 ];

        $campos = array_intersect( $camposto, $camposfrom );
        $campossql = '`' . implode( '`, `', $campos ) . '`';

        return array( $campos, $campossql, $isnumeric );
    }

    /**
     * Transforma una cadena de texto en una función SQL que devolverá la cadena original al ser procesada por el motor SQL.
     * Como se trata de la salida de una funcion no se debe poner comillas alrededor de ésta.
     * <code>
     * $link->query($sql='INSERT INTO personas (nombre) VALUES ('.Config::$helper->quote(_$GET['nombre']).')');
     * </code>
     * Este método evita el uso de la funcion mysqli_real_escape_string ({@link http://es.php.net/mysqli_real_escape_string}) y por consiguiente tener que usar la función stripslashes ({@link http://es.php.net/stripslashes}) cuando se extraen los datos.
     * Mendiante el uso de esta función aseguramos que el valor devuelto por la función MySQL UNHEX será binariamente idéntico al que hemos introducido
     * @param string $cadena Cadena de texto que se quiere transformar
     * @return string
     */
    function quote( $string, $binary = false )
    {
        if ( !is_string( $string ) ) $string = ( string ) $string;

        if ( !isset( $string[ 0 ] ) ) return '\'\'';

        if ( $this->engine == 'mssql' ){
            return 'CONVERT(' . ($binary ? 'VARBINARY' : 'VARCHAR') . '(MAX), 0x' . bin2hex( $string ) . ')';
        }
        else{
            return '0x' . bin2hex( $string );
        }
    }

    /**
     * Transforma una cadena de texto con la que se desea hacer una comparación con el comando LIKE (GRANT o REVOKE) en la salida de la función MySQL UNHEX.
     * Debido a que el texto se obtendrá como salida de una función habrá que concatenar si queremos añadir más texto al LIKE:
     * <code>
     * $res=$link->fetchQuery('SELECT nombre FROM personas WHERE apellido LIKE '.Config::$helper->quoteLGR($_GET['apellido'],'','%'));
     * </code>
     * @param string $s Cadena de texto que se quiere transformar
     * @return string
     */
    function quoteLGR( $s, $prev = null, $post = null )
    { //LIKE GRANT REVOKE
        if ( $this->engine == 'mssql' ) {

            if ( empty( $prev ) && empty( $post ) )
                    return 'CONVERT(VARCHAR(MAX), 0x' . bin2hex( addcslashes( $s,
                                        '%_' ) ) . ')';
            else if ( empty( $prev ) )
                    return 'CONVERT(VARCHAR(MAX), 0x' . bin2hex( addcslashes( $s,
                                        '%_' ) . $post ) . ')';
            else if ( empty( $post ) )
                    return 'CONVERT(VARCHAR(MAX), 0x' . bin2hex( $prev . addcslashes( $s,
                                        '%_' ) ) . ')';
            else
                    return 'CONVERT(VARCHAR(MAX), 0x' . bin2hex( $prev . addcslashes( $s,
                                        '%_' ) . $post ) . ')';
        }
        else {

            if ( empty( $prev ) && empty( $post ) )
                    return '0x' . bin2hex( addcslashes( $s, '%_' ) );
            else if ( empty( $prev ) )
                    return '0x' . bin2hex( addcslashes( $s, '%_' ) . $post );
            else if ( empty( $post ) )
                    return '0x' . bin2hex( $prev . addcslashes( $s, '%_' ) );
            else
                    return '0x' . bin2hex( $prev . addcslashes( $s, '%_' ) . $post );
        }
    }

    /**
     * Transforma una cadena que supuestamente tiene un número en un número
     * El caso de no entender lo que llega devuelve 0.
     * <code>
     * $res=$link->fetchQuery('SELECT nombre FROM personas WHERE id='.Config::$helper->quoteNum($_GET['id']));
     * </code>
     * @param string $num Cadena de texto con el número
     * @return float
     */
    static function quoteNumber( $num )
    {
        return ( float ) $num;
    }


}